package com.onpositive.data.serializer.writers;

import java.io.DataOutputStream;
import java.io.IOException;

public abstract class AbstractWriter<T> {

	protected static final int INT_SIZE = 4;
	protected static final int FLOAT_SIZE = 4;
	protected static final int DOUBLE_SIZE = 8;
	protected static final int CHAR_SIZE = 2;

	protected Class<T> clazz;
	protected DataOutputStream stream;

	private int dataOffset;

	public Class<T> getClazz() {
		return clazz;
	}

	public AbstractWriter(Class<T> clazz) {
		super();
		this.clazz = clazz;
	}

	public final void checkedAppend(Object value) {
		append(clazz.cast(value));
	}

	protected abstract void append(T value);

	public void serialize(DataOutputStream stream) throws IOException {
		dataOffset = 0;
		this.stream = stream;
		writeIntToStream(getID());
		doSerialize();
	}

	protected void passStream(AbstractWriter writer) throws IOException {
		writer.serialize(stream);
	}

	final protected void writeIntToStream(int value) {
		try {
			this.stream.writeInt(value);
			dataOffset += INT_SIZE;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	final protected void writeFloatToStream(float value) {
		try {
			this.stream.writeFloat(value);
			dataOffset += FLOAT_SIZE;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	final protected void writeDoubleToStream(double value) {
		try {
			this.stream.writeDouble(value);
			dataOffset += DOUBLE_SIZE;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	final protected void writeCharToStream(char value) {
		try {
			this.stream.writeChar(value);
			dataOffset += CHAR_SIZE;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	final protected void writeByteToStream(byte value) {
		try {
			this.stream.writeByte(value);
			dataOffset += CHAR_SIZE;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	final protected void writeStringUTFToStream(String value) {
		try {
			this.stream.writeUTF(value);
			dataOffset += (2 + value.getBytes("UTF-8").length);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	final protected void writeByteArrayToStream(byte[] value) {
		try {
			this.stream.write(value);
			dataOffset += value.length;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	abstract protected int getID();

	abstract protected void doSerialize();

	protected final int getDataOffset() {
		return dataOffset;
	}

	protected void closeStream() {
		try {
			stream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// stream can be given to object collection only
	protected DataOutputStream getStream() {
		return getID() == ObjectCollectionWriter.OBJECT_COLLECTION_ID ? this.stream	: null;
	}

}
